#include "LineEntity.h"
#include "GeoUtil.h"
#include "Cache.h"
#include "Global.h"

void LineEntity::create(LineTag tag) {
	const auto& LineARG = Cache::lineArg(tag);
	const auto& Eq = LineARG.eq;
	const auto& Color = LineARG.color;
	switch (LineARG.style) {
	case LineStyle::Solid:
		fillOneLineData(Eq, SOLID, Color);
	    break;
	case LineStyle::Dashed:
		fillOneLineData(Eq, DASHED, Color);
	    break;
	case LineStyle::Dashed_Solid:
		fillOneLineData(Eq.sideEq(LineEq::Left), DASHED, Color, ASCHILD);
		fillOneLineData(Eq.sideEq(LineEq::Right), SOLID, Color, ASCHILD);
	    break;
	case LineStyle::Solid_Dashed:
		fillOneLineData(Eq.sideEq(LineEq::Left), SOLID, Color, ASCHILD);
		fillOneLineData(Eq.sideEq(LineEq::Right), DASHED, Color, ASCHILD);
	    break;
	case LineStyle::Double_Dashed:
		fillOneLineData(Eq.sideEq(LineEq::Left), DASHED, Color, ASCHILD);
		fillOneLineData(Eq.sideEq(LineEq::Right), DASHED, Color, ASCHILD);
	    break;
	case LineStyle::Double_Solid:
		fillOneLineData(Eq.sideEq(LineEq::Left), SOLID, Color, ASCHILD);
		fillOneLineData(Eq.sideEq(LineEq::Right), SOLID, Color, ASCHILD);
	    break;
	case LineStyle::Deceleration_Solid:
		fillOneLineData(Eq.sideEq(LineEq::Tripple_Left), SOLID, Color, ASCHILD, 1.0);
		fillOneLineData(Eq.sideEq(LineEq::Tripple_Right), SOLID, Color, ASCHILD, -1.0);
		fillOneLineData(Eq, DASHED, Color, ASCHILD);
	    break;
	case LineStyle::Deceleration_Dashed:
		fillOneLineData(Eq.sideEq(LineEq::Tripple_Left), SOLID, Color, ASCHILD, 1.0);
		fillOneLineData(Eq.sideEq(LineEq::Tripple_Right), SOLID, Color, ASCHILD, -1.0);
		fillOneLineData(Eq, SOLID, Color, ASCHILD);
	    break;
	case LineStyle::RoadEdge:
		fillRoadEdge(Eq);
	    break;
	}
}

void LineEntity::fillOneLineData(const LineEq& oneLineEq, bool isSolid, const CColor& color, bool asChild, float slopeRatioByWidth) {
	auto e = this;
	if (asChild) e = new LineEntity(this);

	CVector<P> vertices;
	if (isSolid) {
		e->setPrimitiveType(PrimitiveType::TRIANGLE_STRIP);
		GeoUtil::createSolidLine(vertices, oneLineEq);
	} else {
		e->setPrimitiveType(PrimitiveType::TRIANGLES);
		CVector<UInt> indices;
		GeoUtil::createDashedLine(vertices, indices, oneLineEq, slopeRatioByWidth, Global::dashedVOffset());
		e->setIndices(indices);
	}

	//e->mixBackground(Global::currentBgTexture());// setFadeOutEdgeEnabled has already do it!

	e->setVertices(vertices, Config::Y_Line);

	e->setColor(color);

	e->setFadeOutEdgeEnabled(oneLineEq.zEnd, Config::Line_FadeByEndZOffset);

	e->install(Config::Shader_Line);
}

void LineEntity::fillRoadEdge(const LineEq& lineEq) {
	CVector<P3D> vertices;
	const auto EdgeW = Config::Line_RoadEdge_Width;
	const auto EdgeH = Config::Line_RoadEdge_Height;
	auto _eq = lineEq;
	_eq.zStart += EdgeW * 2;// add offset because: three-dimensional rather than 2d-flat in shape, to avoid the starter-rect-shape expose

	const auto Style = Config::Line_RoadEdge_Style;
	if (Style == RoadEdgeStyle::Dashed) {
		CVector<UInt> indices;

		float dashedVOffset = Global::dashedVOffset() * Config::Line_RoadEdgeDashed_SegLen / Config::Line_Dashed_SegLen;
		GeoUtil::createRoadEdge(vertices, indices, _eq, EdgeW, EdgeH, dashedVOffset);

		setIndices(indices);

		setCullingFaceEnabled(false);

		setPrimitiveType(PrimitiveType::TRIANGLES);
	} else if (Style == RoadEdgeStyle::Continuous) {
		GeoUtil::createRoadEdge(vertices, _eq, EdgeW, EdgeH);
		setPrimitiveType(PrimitiveType::TRIANGLE_STRIP);
	}

	setVertices(vertices);

	/**@note two different color nearby may lead to obvious jagged artifacts!*/
	addUniform("edge.sideColor", CColor(Config::Line_RoadEdge_SideColor));// side-color
	addUniform("edge.upY", Config::Y_Line + EdgeH);

	setColor(Config::Line_RoadEdge_UpColor);// up-color

	setFadeOutEdgeEnabled(_eq.zEnd, Config::Line_FadeByEndZOffset);

	install(Config::Shader_RoadEdge);
}